#ifndef PACKETQUEUE_H
#define PACKETQUEUE_H
#include <iostream>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include "ffinc.h"
using namespace std;

class PacketQueue
{
    // 队列节点
    struct MyAVPacketList {
        AVPacket *pkt;      // 媒体包
        int serial;         // 播放序列
    };
public:
    PacketQueue();
    ~PacketQueue();

public:
    /**
     * @brief 创建并初始化队列
     * @return 0: 成功 其他: 错误代码
     */
    int packet_queue_init();
    /**
     * @brief 销毁队列，释放资源
     */
    void packet_queue_destroy();
    /**
     * @brief 获取队列当前大小
     */
    uint32_t packet_queue_size() { return size; }
    /**
     * @brief 队列是否已经退出
     */
    bool packet_queue_isAbort() { return isAbort; }
    /**
     * @brief 获取队列当前packet数量
     */
    uint32_t packet_queue_packet_num() { return pktNum; }
    /**
     * @brief 获取队列所有元素的数据播放持续时间
     */
    int64_t packet_queue_duration() { return duration; }
    /**
     * @brief 获取队列播放序列
     */
    uint32_t packet_queue_serial() { return serial; }
    /**
     * @brief 将队列中的所有节点清除
     */
    void packet_queue_flush();
    /**
     * @brief 启动队列
     */
    void packet_queue_start();
    /**
     * @brief 中止队列
     */
    void packet_queue_abort();
    /**
     * @brief 往队列中插入一个节点
     * @param pkt: 媒体包
     * @return 0: 成功 -1: 失败
     */
    int packet_queue_put(AVPacket *pkt);
    /**
     * @brief 放入“空包”。放入空包意味着流的结束，一般在媒体数据读取完成的时候放入空包。
     *          放入空包，目的是为了冲刷解码器，将解码器里面所有frame都读取出来
     * @param pkt: 空包
     * @param stream_index: 流索引
     * @return 0: 成功 -1: 失败
     */
    int packet_queue_put_nullpacket(AVPacket *pkt, int streamIndex);
    /**
     * @brief 从队列中取一个节点
     * @param pkt[out]: 媒体包
     * @param block: 是否阻塞调用 1: 阻塞 0: 非阻塞
     * @param serial[out]: 即MyAVPacketList.serial
     * @return -1: 队列以中止 0: 没有可取 1: 成功取出一个节点
     */
    int packet_queue_get(AVPacket *pkt, int block, uint32_t *serial);

private:
    /**
     * @brief 往队列中插入一个节点，packet_queue_put的主要实现
     * @param pkt: 媒体包
     * @return 0: 成功 -1: 失败
     */
    int packet_queue_put_private(AVPacket *pkt);

private:
    AVFifoBuffer *queue;    // ffmpeg内部一个非常简单的循环缓冲区FIFO实现
    uint32_t pktNum;        // 包数量，也就是队列元素数量
    uint32_t size;          // 队列所有元素的数据大小总和
    int64_t duration;       // 队列所有元素的数据播放持续时间
    bool isAbort;           // 用户退出请求标志
    uint32_t serial;        // 播放序列号，和MyAVPacketList的serial作用相同，但改变的时序稍微有点不同
    mutex _mutex;           // 用于维持PacketQueue的多线程安全
    condition_variable  _condition; //队列内部信号量，用于读、写线程相互通知
};

#endif // FFQUEUE_H
